package util;

import annotation.AliasFor;
import exception.SoiRuntimeException;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Deque;

/**
 * @className: AnnotationUtil
 * @author: ocxz1
 * @date: 2025/3/11 下午8:17
 * @Version: 1.0
 * @description: tmp-study
 */
public class AnnotationUtil {

    /**
     * 判断是否有注解，限制递归深度为3层
     * @param clazz 需要检查的类
     * @param annotationClass 注解类
     * @return true/false
     */
    public static <T extends Annotation> boolean hasAnnotation(Class<?> clazz, Class<T> annotationClass) {
        Annotation annotation = getAnnotation(clazz, annotationClass);
        return annotation != null;
    }

    public static <T extends Annotation> T getAnnotation(Class<?> clazz, Class<T> annotationClass) {
        // 使用栈来存储需要检查的注解类型和当前深度
        Deque<AnnotationCheck> stack = new ArrayDeque<>();
        stack.push(new AnnotationCheck(clazz, 0));

        while (!stack.isEmpty()) {
            AnnotationCheck currentCheck = stack.pop();
            Class<?> currentClass = currentCheck.clazz;
            int currentDepth = currentCheck.depth;

            // 检查当前类是否包含直接注解
            if (currentClass.isAnnotationPresent(annotationClass)) {
                return currentClass.getAnnotation(annotationClass);
            }

            // 如果当前深度达到3层，则不再继续检查
            if (currentDepth >= 3) {
                continue;
            }

            // 获取当前类的所有注解
            Annotation[] annotations = currentClass.getAnnotations();

            // 将所有注解的类型压入栈中，并增加深度
            for (Annotation annotation : annotations) {
                stack.push(new AnnotationCheck(annotation.annotationType(), currentDepth + 1));
            }
        }

        return null;
    }

    public static <T extends Annotation> T getAnnotation(Method method, Class<T> annotationClass) {
        if (method.isAnnotationPresent(annotationClass)) {
            return method.getAnnotation(annotationClass);
        }
        return null;
    }


    public static <T extends Annotation> Object getAliasedValue(Class<?> clazz, Class<T> targetAnnotationClass, String...methodNames) {
        try {
            if (methodNames == null || methodNames.length == 0) {
                methodNames = new String[]{"value"};
            }
            String methodName = methodNames[0];

            Annotation[] annotations = clazz.getAnnotations();
            for (Annotation annotation : annotations) {
                if (annotation.getClass().equals(targetAnnotationClass)) {
                    return annotation.getClass().getMethod(methodName).invoke(annotation);
                }

                if (annotation.annotationType().isAnnotationPresent(targetAnnotationClass)) {
                    Method[] methods = annotation.annotationType().getDeclaredMethods();
                    for (Method method : methods) {
                        if (!method.getName().equals(methodName)) {
                            continue;
                        }
                        AliasFor aliasFor = method.getAnnotation(AliasFor.class);
                        if (aliasFor != null) {

                            String attribute = aliasFor.attribute();
                            if (attribute == null || attribute.isEmpty()) {
                                attribute = method.getName();
                            }
                            Method targetMethod = annotation.annotationType().getMethod(attribute);
                            return targetMethod.invoke(annotation);
                        } else {
                            return method.invoke(annotation);
                        }
                    }
                }
            }
        } catch (Exception e) {
            throw new SoiRuntimeException(e);
        }
        return null;
    }


                                                                                     // 辅助类，用于存储注解类型和当前深度
    private static class AnnotationCheck {
        Class<?> clazz;
        int depth;

        AnnotationCheck(Class<?> clazz, int depth) {
            this.clazz = clazz;
            this.depth = depth;
        }
    }


}
